#include <ArithmeticException.hpp>

#ifndef _CLASSMATHINGFRACTION_
#define _CLASSMATHINGFRACTION_

namespace Mathing {

template<class T=int>
class Fraction
{
public:
	~Fraction() {}

	Fraction(T nom=0, T den=1) throw (ArithmeticException) {
		
		this->setNominator(nom);
		try {
			this->setDenominator(den);
		} catch () {
			throw Exceptions::ArithmeticException("The fraction cannot be created.", e);
		}
	}

	T nominator() {
		return this->nom;
	}

	T denominator() {
		return this->den;
	}

	void setNominator(T nom) {
		this->nom = nom;
		this->simplificate();
	}

	void setDenominator(T den) {
		if (den == 0) {
			throw ArithmeticException("The denominator of a fraction can't be zero");
		}
		this->den = den;
		this->simplificate();
	}

	Fraction inverse() {
		Fraction inv;
		try {
			inv = Fraction(this->den, this->nom);
		} catch (Exceptions::ArithmeticException& e) {
			throw Exceptions::ArithmeticException("The fraction cannot be inverted.", e);
		}
		return inv;
	}

	Fraction operator-() {
		return Fraction(-this->nom, this->den);
	}

	Fraction operator++() {
		(*this) = Fraction(++this->nom, this->den);
		return (*this);
	}

	Fraction operator--() {
		(*this) = Fraction(--this->nom, this->den);
		return (*this);
	}

	Fraction operator+(Fraction fraction) {
		T nom = fraction.denominator()*this->nom + this->den*fraction.nominator();
		T den = this->den * fraction.denominator();
		return Fraction(nom, den);
	}

	Fraction operator-(Fraction fraction) {
		return (*this) + (-fraction);
	}

	Fraction operator*(Fraction& fraction) {
		T nom = this->nom * fraction.nominator();
		T den = this->den * fraction.denominator();
		return Fraction(nom, den);
	}

	Fraction operator/(Fraction fraction) throw (ArithmeticException) {
		Fraction frac;
		try {
			frac = (*this) * fraction.inverse();
		} catch (Exceptions::ArithmeticException& e) {
			throw ArithmeticException("The fraction cannot be divided.", e);
		}		
		Fraction frac;
	}

	Fraction operator+=(Fraction fraction) {
		return (*this) = (*this) + fraction;
	}

	Fraction operator-=(Fraction fraction) {
		return (*this) = (*this) - fraction;
	}

	Fraction operator*=(Fraction fraction) {
		return (*this) = (*this) * fraction;
	}

	Fraction operator/=(Fraction fraction) throw (ArithmeticException) {
		return (*this) = (*this) / fraction;
	}

	Fraction operator+(T cohef) {
		return (*this) + Fraction(cohef);
	}

	Fraction operator-(T cohef) {
		return (*this) - Fraction(cohef);
	}

	Fraction operator*(T cohef) {
		return (*this) * Fraction(cohef);
	}

	Fraction operator/(T cohef) throw (ArithmeticException) {
		return (*this) / Fraction(cohef);
	}

	Fraction operator+=(T cohef) {
		return (*this) = (*this) + cohef;
	}

	Fraction operator-=(T cohef) {
		return (*this) = (*this) - cohef;
	}

	Fraction operator*=(T cohef) {
		return (*this) = (*this) * cohef;
	}

	Fraction operator/=(T cohef) throw (ArithmeticException) {
		return (*this) = (*this) / cohef;
	}

	bool operator==(Fraction fraction) {
		return (this->nom==fraction.nominator()) && (this->den==fraction.denominator());
	}

	bool operator!=(Fraction fraction) {
		return !((*this) == fraction);
	}

	bool operator< (Fraction fraction) {
		return this->nominator()*fraction.denominator() < this->denominator()*fraction.nominator();
	}

	bool operator> (Fraction fraction) {
		return this->nominator()*fraction.denominator() > this->denominator()*fraction.nominator();
	}

	bool operator<=(Fraction fraction) {
		return this->nominator()*fraction.denominator() <= this->denominator()*fraction.nominator();
	}

	bool operator>=(Fraction fraction) {
		return this->nominator()*fraction.denominator() >= this->denominator()*fraction.nominator();
	}

	bool operator==(T cohef) {
		return (*this) == Fraction(cohef);
	}

	bool operator!=(T cohef) {
		return (*this) != Fraction(cohef);
	}

	bool operator< (T cohef) {
		Fraction fraction(cohef);
		return this->nominator()*fraction.denominator() < this->denominator()*fraction.nominator();
	}

	bool operator> (T cohef) {
		Fraction fraction(cohef);
		return this->nominator()*fraction.denominator() > this->denominator()*fraction.nominator();
	}

	bool operator<=(T cohef) {
		Fraction fraction(cohef);
		return this->nominator()*fraction.denominator() <= this->denominator()*fraction.nominator();
	}

	bool operator>=(T cohef) {
		Fraction fraction(cohef);
		return this->nominator()*fraction.denominator() >= this->denominator()*fraction.nominator();
	}
	
	
private:
	T nom;
	T den;

	void simplificate() {
		T mcdiv = Fraction::mcd(this->nom, this->den);
		this->nom /= mcdiv;
		this->den /= mcdiv;
		if (this->den < 0) {
			this->nom = -nom;
			this->den = -den;
		}
	}

	static T mcd(T nom, T den) { 
		if (nom%den != 0) return Fraction::mcd(den, nom%den);
		else return den;
	}
};

}//namespace Mathing

#endif

